{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "4d6ec946",
   "metadata": {},
   "source": [
    "# 第八章 向量代数与空间解析几何\n",
    "## 第八节 多元的极值及其求法\n",
    "### 一 多元函数的极值及最大值及最小值\n",
    "定义设函数z=f(x,y)的定义域为D,P₀(x。,y。)为D的内点.若存在P。的某个邻域U(P₀)CD,使得对于该邻域内异于P。的任何点(x,y),都有\n",
    "f(x,y)<f(x₀,y。),\n",
    "则称函数f(x,y)在点(x₀,y。)有极大值f(x₀,y。),点(x₀,yo)称为函数f(x,y)的极大值点；若对于该邻域内异于P。的任何点(x,y),都有\n",
    "f(x,y)>f(x₀,y。),\n",
    "则称函数f(x,y)在点(x₀,y。)有极小值f(x₀,y。),点(x₀,y。)称为函数f(x,y)的极小值点.极大值与极小值统称为极值.使得函数取得极值的点称为极值点."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b36473e7",
   "metadata": {},
   "source": [
    "例1函数z=3x²+4y²在点(0,0)处有极小值.因为对于点(0,0)的任一邻\n",
    "域内异于(0,0)的点，函数值都为正，而在点(0,0)处的函数值为零.从几何上看这是显然的，因为点(0,0,0)是开口朝上的椭圆抛物面z=3x²+4y²的顶点."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bfdafb66",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "函数在点(0, 0)处的函数值: 0\n",
      "在邻域内点(0.1, 0.1)处的函数值: 0.0700000000000000\n",
      "在邻域内点(-0.1, 0.2)处的函数值: 0.190000000000000\n",
      "在邻域内点(0.2, -0.1)处的函数值: 0.160000000000000\n",
      "从几何上看，z = 3 * x ** 2 + 4 * y ** 2对应的曲面是开口朝上的椭圆抛物面，点(0, 0, 0)是其顶点。\n"
     ]
    }
   ],
   "source": [
    "import sympy\n",
    "\n",
    "# 定义变量\n",
    "x, y = sympy.Symbol('x'), sympy.Symbol('y')\n",
    "# 定义函数\n",
    "z = 3 * x ** 2 + 4 * y ** 2\n",
    "# 查看在点(0, 0)处的函数值\n",
    "value_at_origin = z.subs({x: 0, y: 0})\n",
    "print(\"函数在点(0, 0)处的函数值:\", value_at_origin)\n",
    "\n",
    "# 可以简单示意性地取点(0, 0)邻域内的一些点（示例做法，非严格证明）\n",
    "neighborhood_points = [\n",
    "    (0.1, 0.1), (-0.1, 0.2), (0.2, -0.1)\n",
    "]\n",
    "for point in neighborhood_points:\n",
    "    point_x, point_y = point\n",
    "    point_value = z.subs({x: point_x, y: point_y})\n",
    "    print(f\"在邻域内点({point_x}, {point_y})处的函数值:\", point_value)\n",
    "    \n",
    "# 从几何角度简单描述（这里只是打印提示语句说明形状特征）\n",
    "print(\"从几何上看，z = 3 * x ** 2 + 4 * y ** 2对应的曲面是开口朝上的椭圆抛物面，点(0, 0, 0)是其顶点。\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bfcf1da7",
   "metadata": {},
   "source": [
    "例二例2函数z=-√x²+y²在点(0,0)处有极大值.因为在点(0,0)处函数值\n",
    "为零，而对于点(0,0)的任一邻域内异于(0,0)的点，函数值都为负.点(0,0,0)是位于x0y平面下方的锥面z=-√x²+y²的顶点."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a07c3394",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "函数在点(0, 0)处的函数值: 0\n",
      "在邻域内点(0.1, 0.1)处的函数值: -0.141421356237310\n",
      "在邻域内点(-0.1, 0.2)处的函数值: -0.223606797749979\n",
      "在邻域内点(0.2, -0.1)处的函数值: -0.223606797749979\n",
      "从几何上看，z = -sympy.sqrt(x ** 2 + y ** 2)对应的曲面是位于xOy平面下方的锥面，点(0, 0, 0)是其顶点。\n",
      "驻点: []\n"
     ]
    }
   ],
   "source": [
    "import sympy\n",
    "\n",
    "# 定义变量\n",
    "x, y = sympy.Symbol('x'), sympy.Symbol('y')\n",
    "# 定义函数\n",
    "z = -sympy.sqrt(x ** 2 + y ** 2)\n",
    "\n",
    "# 查看在点(0, 0)处的函数值\n",
    "value_at_origin = z.subs({x: 0, y: 0})\n",
    "print(\"函数在点(0, 0)处的函数值:\", value_at_origin)\n",
    "\n",
    "# 简单示意性地取点(0, 0)邻域内的一些点（示例做法，非严格证明）\n",
    "neighborhood_points = [\n",
    "    (0.1, 0.1), (-0.1, 0.2), (0.2, -0.1)\n",
    "]\n",
    "for point in neighborhood_points:\n",
    "    point_x, point_y = point\n",
    "    try:\n",
    "        point_value = z.subs({x: point_x, y: point_y})\n",
    "        print(f\"在邻域内点({point_x}, {point_y})处的函数值:\", point_value)\n",
    "    except:\n",
    "        print(f\"在邻域内点({point_x}, {point_y})处，函数在此处输入无定义（因为根号下不能为负等原因），请更换合适的邻域内点\")\n",
    "\n",
    "# 从几何角度简单描述（这里只是打印提示语句说明形状特征）\n",
    "print(\"从几何上看，z = -sympy.sqrt(x ** 2 + y ** 2)对应的曲面是位于xOy平面下方的锥面，点(0, 0, 0)是其顶点。\")\n",
    "\n",
    "\n",
    "# 以下从更数学分析角度（求偏导数角度简单示意判断，严格完整证明更复杂）\n",
    "# 求关于x的偏导数\n",
    "z_x = sympy.diff(z, x)\n",
    "# 求关于y的偏导数\n",
    "z_y = sympy.diff(z, y)\n",
    "\n",
    "# 寻找驻点，令偏导数为0，解方程组\n",
    "critical_points = sympy.solve([z_x, z_y], [x, y], dict=True)\n",
    "print(\"驻点:\", critical_points)\n",
    "\n",
    "# 求二阶偏导数（部分情况这里会涉及到复杂表达式和更多数学判断处理，这里仅简单示意）\n",
    "z_xx = sympy.diff(z_x, x)\n",
    "z_yy = sympy.diff(z_y, y)\n",
    "z_xy = sympy.diff(z_x, y)\n",
    "\n",
    "# 定义判别式相关计算（完整严格判断要考虑更多边界情况等，这里简单示意）\n",
    "discriminant = z_xx * z_yy - z_xy ** 2\n",
    "\n",
    "# 对于每个驻点进行判断是否为极大值（这里主要就是判断点(0, 0) ）\n",
    "for point in critical_points:\n",
    "    point_x_value = point[x]\n",
    "    point_y_value = point[y]\n",
    "    A_value = z_xx.subs({x: point_x_value, y: point_y_value})\n",
    "    D_value = discriminant.subs({x: point_x_value, y: point_y_value})\n",
    "    if A_value < 0 and D_value > 0:\n",
    "        print(f\"点({point_x_value}, {point_y_value})是极大值点\")\n",
    "    else:\n",
    "        print(f\"点({point_x_value}, {point_y_value})不是极大值点\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20505286",
   "metadata": {},
   "source": [
    "例三函数z=xy在点(0,0)处既不取得极大值也不取得极小值.因为在点\n",
    "(0,0)处的函数值为零，而在点(0,0)的任一邻域内，总有使函数值为正的点，也有使函数值为负的点."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ef04519f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "函数在点(0, 0)处的函数值: 0\n",
      "在邻域内取的这些点中，函数值为正的点有4个，函数值为负的点有4个\n",
      "由此可简单示意在点(0, 0)的任一邻域内，总有使函数值为正的点，也有使函数值为负的点，说明该点既不取得极大值也不取得极小值。\n"
     ]
    }
   ],
   "source": [
    "import sympy\n",
    "\n",
    "# 定义变量\n",
    "x, y = sympy.Symbol('x'), sympy.Symbol('y')\n",
    "# 定义函数\n",
    "z = x * y\n",
    "\n",
    "# 查看在点(0, 0)处的函数值\n",
    "value_at_origin = z.subs({x: 0, y: 0})\n",
    "print(\"函数在点(0, 0)处的函数值:\", value_at_origin)\n",
    "\n",
    "# 简单示意性地取点(0, 0)邻域内的一些点（示例做法，非严格证明）\n",
    "neighborhood_points = [\n",
    "    (0.1, 0.1), (-0.1, 0.1), (0.1, -0.1), (-0.1, -0.1), (0.2, 0.3), (-0.2, 0.3), (0.2, -0.3), (-0.2, -0.3)\n",
    "]\n",
    "positive_count = 0\n",
    "negative_count = 0\n",
    "for point in neighborhood_points:\n",
    "    point_x, point_y = point\n",
    "    point_value = z.subs({x: point_x, y: point_y})\n",
    "    if point_value > 0:\n",
    "        positive_count += 1\n",
    "    elif point_value < 0:\n",
    "        negative_count += 1\n",
    "\n",
    "print(f\"在邻域内取的这些点中，函数值为正的点有{positive_count}个，函数值为负的点有{negative_count}个\")\n",
    "print(\"由此可简单示意在点(0, 0)的任一邻域内，总有使函数值为正的点，也有使函数值为负的点，说明该点既不取得极大值也不取得极小值。\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1b636e9",
   "metadata": {},
   "source": [
    "定理1(必要条件)设函数z=f(x,y)在点(x₀,yo)具有偏导数，且在点(x₀,y。)处有极值，则有\n",
    "f.(x₀,yo)=0,f,(x₀,y。)=0.\n",
    "证不妨设z=f(x,y)在点(x₀,y₀)处有极大值.依照极大值的定义，在点(x₀,yo)的某邻域内异于(x₀,yo)的点(x,y)都适合不等式\n",
    "f(x,y)<f(x₀,y。).\n",
    "特殊地，在该邻域内取y=y。而x≠x。的点，也应适合不等式\n",
    "f(x,yo)<f(x₀,y。).这表明一元函数f(x,y。)在x=x。处取得极大值，因而必有\n",
    "f.(x₀,yo)=0.\n",
    "类似可证\n",
    "f,(x。,yo)=0.\n",
    "从几何上看，这时如果曲面z=f(x,y)在点(x₀,yo,zo)处有切平面，则切平面z-zo=f(x₀,yo)(x-x₀)+f,(x₀,yo)(y-yo)\n",
    "成为平行于x0y坐标面的平面z-zo=0.\n",
    "类似地推得，如果三元函数u=f(x,y,z)在点(x₀,yo,zo)具有偏导数，那么它在点(x₀,yo,zo)具有极值的必要条件为\n",
    "f.(x₀,yo,zo)=0,f,(x₀,yo,z)=0,f₂(x。,yo,zo)=0.\n",
    "仿照一元函数，凡是能使f(x,y)=0,f,(x,y)=0同时成立的点(xo,y。)称为函数z=f(x,y)的驻点.从定理1可知，具有偏导数的函数的极值点必定是驻点.但函数的驻点不一定是极值点，例如，点(0,0)是函数z=xy的驻点，但函数在该点并无极值."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e5ec8c6a",
   "metadata": {},
   "source": [
    "定理2(充分条件)设函数z=f(x,y)在点(x₀,y₀)的某邻域内连续且有一阶及二阶连续偏导数，又f₂(x₀,yo)=0,f,(xo。,yo)=0,令\n",
    "fx(x₀,yo)=A,fy(x₀,yo)=B,f,,(x₀,yo)=C,\n",
    "则f(x,y)在(x₀,y。)处是否取得极值的条件如下：\n",
    "(1)AC-B²>0时具有极值，且当A<0时有极大值，当A>0时有极小值；\n",
    "(2)AC-B²<0时没有极值；\n",
    "(3)AC-B²=0时可能有极值，也可能没有极值，还需另作讨论.\n",
    "这个定理现在不证①.利用定理1、定理2,我们把具有二阶连续偏导数的函数z=f(x,y)的极值的求法叙述如下：\n",
    "第一步解方程组\n",
    "f,(x,y)=0,f,(x,y)=0,\n",
    "求得一切实数解，即可求得一切驻点.\n",
    "第二步对于每一个驻点(x。,y₀),求出二阶偏导数的值A、B和C.\n",
    "第三步定出AC-B²的符号，按定理2的结论判定f(x₀,yo)是不是极值、是极大值还是极小值."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d330da7",
   "metadata": {},
   "source": [
    "例4求函数f(x,y)=x³-y³+3x²+3y²-9x的极值."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4ca2e66",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "驻点: [{x: -3, y: 0}, {x: -3, y: 2}, {x: 1, y: 0}, {x: 1, y: 2}]\n",
      "点(-3, 0)不是极值点，函数在此点的值为27\n",
      "点(-3, 2)是极大值点，极大值为31\n",
      "点(1, 0)是极小值点，极小值为-5\n",
      "点(1, 2)不是极值点，函数在此点的值为-1\n"
     ]
    }
   ],
   "source": [
    "import sympy\n",
    "\n",
    "# 定义变量\n",
    "x, y = sympy.Symbol('x'), sympy.Symbol('y')\n",
    "# 定义函数\n",
    "f = x ** 3 - y ** 3 + 3 * x ** 2 + 3 * y ** 2 - 9 * x\n",
    "\n",
    "# 求关于x的偏导数\n",
    "f_x = sympy.diff(f, x)\n",
    "# 求关于y的偏导数\n",
    "f_y = sympy.diff(f, y)\n",
    "\n",
    "# 寻找驻点，令偏导数为0，解方程组\n",
    "critical_points = sympy.solve([f_x, f_y], [x, y], dict=True)\n",
    "print(\"驻点:\", critical_points)\n",
    "\n",
    "# 求二阶偏导数\n",
    "f_xx = sympy.diff(f_x, x)\n",
    "f_yy = sympy.diff(f_y, y)\n",
    "f_xy = sympy.diff(f_x, y)\n",
    "\n",
    "# 定义判别式相关计算\n",
    "discriminant = f_xx * f_yy - f_xy ** 2\n",
    "\n",
    "# 对于每个驻点进行判断是否为极值点\n",
    "for point in critical_points:\n",
    "    point_x_value = point[x]\n",
    "    point_y_value = point[y]\n",
    "    A_value = f_xx.subs({x: point_x_value, y: point_y_value})\n",
    "    D_value = discriminant.subs({x: point_x_value, y: point_y_value})\n",
    "    if D_value > 0:\n",
    "        if A_value > 0:\n",
    "            print(f\"点({point_x_value}, {point_y_value})是极小值点，极小值为{f.subs({x: point_x_value, y: point_y_value})}\")\n",
    "        elif A_value < 0:\n",
    "            print(f\"点({point_x_value}, {point_y_value})是极大值点，极大值为{f.subs({x: point_x_value, y: point_y_value})}\")\n",
    "        else:\n",
    "            print(f\"点({point_x_value}, {point_y_value})可能是极值点，需进一步判断，函数在此点的值为{f.subs({x: point_x_value, y: point_y_value})}\")\n",
    "    elif D_value < 0:\n",
    "        print(f\"点({point_x_value}, {point_y_value})不是极值点，函数在此点的值为{f.subs({x: point_x_value, y: point_y_value})}\")\n",
    "    else:\n",
    "        print(f\"点({point_x_value}, {point_y_value})可能是极值点，需进一步判断，函数在此点的值为{f.subs({x: point_x_value, y: point_y_value})}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3478980c",
   "metadata": {},
   "source": [
    "例5某厂要用铁板做成一个体积为2m³的有盖长方体水箱.问当长、宽和高各取怎样的尺寸时，才能使用料最省."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffea7592",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "驻点: [{x: 2**(1/3), y: 2**(1/3)}, {x: -2**(1/3)/2 - 2**(1/3)*sqrt(3)*I/2, y: -2**(1/3)/2 - 2**(1/3)*sqrt(3)*I/2}, {x: -2**(1/3)/2 + 2**(1/3)*sqrt(3)*I/2, y: -2**(1/3)/2 + 2**(1/3)*sqrt(3)*I/2}]\n",
      "当长为2**(1/3)米，宽为2**(1/3)米，高为2**(1/3)米时，用料最省。\n"
     ]
    }
   ],
   "source": [
    "import sympy\n",
    "\n",
    "# 设长方体水箱的长、宽、高分别为x, y, z（单位：米）\n",
    "x, y = sympy.Symbol('x'), sympy.Symbol('y')\n",
    "\n",
    "# 已知体积为2立方米，根据长方体体积公式 V = x * y * z，可得 z = 2 / (x * y)\n",
    "z = 2 / (x * y)\n",
    "\n",
    "# 水箱的表面积S（用料多少与表面积相关），长方体表面积公式 S = 2(xy + yz + zx)\n",
    "S = 2 * (x * y + y * (2 / (x * y)) + (2 / (x * y)) * x)\n",
    "S = 2 * (x * y + 2 / x + 2 / y)\n",
    "\n",
    "# 求关于x的偏导数\n",
    "S_x = sympy.diff(S, x)\n",
    "# 求关于y的偏导数\n",
    "S_y = sympy.diff(S, y)\n",
    "\n",
    "# 寻找驻点，令偏导数为0，解方程组\n",
    "critical_points = sympy.solve([S_x, S_y], [x, y], dict=True)\n",
    "print(\"驻点:\", critical_points)\n",
    "\n",
    "# 实际问题中驻点唯一，该驻点就是使表面积最小（用料最省）的点，取出对应的长、宽值\n",
    "length = critical_points[0][x]\n",
    "width = critical_points[0][y]\n",
    "height = 2 / (length * width)\n",
    "\n",
    "print(f\"当长为{length}米，宽为{width}米，高为{height}米时，用料最省。\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc60e60f",
   "metadata": {},
   "source": [
    "### 二 条件极值  拉格朗日乘数法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c88dea4d",
   "metadata": {},
   "source": [
    "拉格朗日乘数法 要找函数z=f(x,y)在附加条件φ(x,y)=0下的可能极值点，可以先作拉格朗日函数\n",
    "L(x,y)=f(x,y)+λφ(x,y),\n",
    "其中λ为参数.求其对x与y的一阶偏导数，并使之为零，然后与方程(8-2)联立起来：\n",
    "(8-8)\n",
    "由这方程组解出x、y及λ,这样得到的(x,y)就是函数f(x,y)在附加条件φ(x,y)=0下的可能极值点.\n",
    "这方法还可以推广到自变量多于两个而条件多于一个的情形.例如，要求函数\n",
    "u=f(x,y,z,t)\n",
    "在附加条件\n",
    "φ(x,y,z,t)=0,ψ(x,y,z,t)=0\n",
    "(8-9)\n",
    "下的极值，可以先作拉格朗日函数\n",
    "L(x,y,z,t)=f(x,y,z,t)+λφ(x,y,z,t)+μψ(x,y,z,t),\n",
    "其中λ,μ均为参数，求其一阶偏导数，并使之为零，然后与(8-9)中的两个方程联立起来求解，这样得出的(x,y,z,t)就是函数f(x,y,z,t)在附加条件(8-9)下的可能极值点.\n",
    "至于如何确定所求得的点是否极值点，在实际问题中往往可根据问题本身的性质来判定."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e97788ce",
   "metadata": {},
   "source": [
    "例题七 求表面积为a²而体积为最大的长方体的体积."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b972b7a5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "驻点: [{x: -sqrt(6)*a/6, y: -sqrt(6)*a/6, z: -sqrt(6)*a/6, lambda_: -sqrt(6)*a/24}, {x: sqrt(6)*a/6, y: sqrt(6)*a/6, z: sqrt(6)*a/6, lambda_: sqrt(6)*a/24}]\n",
      "当长方体的长为-0.408248290463863*a，宽为-0.408248290463863*a，高为-0.408248290463863*a时，体积最大，最大体积为-0.0680413817439772*a**3\n"
     ]
    }
   ],
   "source": [
    "import sympy\n",
    "\n",
    "# 定义变量，长方体的长、宽、高分别设为x、y、z，拉格朗日乘数设为lambda_（由于lambda是Python关键字，所以这里用lambda_表示），同时把a也定义为符号变量\n",
    "x, y, z, lambda_, a = sympy.Symbol('x'), sympy.Symbol('y'), sympy.Symbol('z'), sympy.Symbol('lambda_'), sympy.Symbol('a')\n",
    "\n",
    "# 定义长方体的体积函数V = x * y * z\n",
    "V = x * y * z\n",
    "\n",
    "# 已知长方体表面积为a²，根据表面积公式可得约束条件g(x, y, z) = 2*(xy + yz + zx) - a² = 0\n",
    "g = 2 * (x * y + y * z + z * x) - a ** 2\n",
    "\n",
    "# 构建拉格朗日函数L(x, y, z, lambda_) = V - lambda_ * g\n",
    "L = V - lambda_ * g\n",
    "\n",
    "# 分别求拉格朗日函数关于x、y、z、lambda_的偏导数\n",
    "L_x = sympy.diff(L, x)\n",
    "L_y = sympy.diff(L, y)\n",
    "L_z = sympy.diff(L, z)\n",
    "L_lambda_ = sympy.diff(L, lambda_)\n",
    "\n",
    "# 令这些偏导数都等于0，组成方程组并求解驻点\n",
    "critical_points = sympy.solve([L_x, L_y, L_z, L_lambda_], [x, y, z, lambda_], dict=True)\n",
    "\n",
    "# 输出驻点信息（实际问题中驻点对应使体积最大的情况）\n",
    "print(\"驻点:\", critical_points)\n",
    "\n",
    "# 提取驻点中x、y、z的值（这里假设只有一个驻点符合实际情况）\n",
    "x_value = critical_points[0][x]\n",
    "y_value = critical_points[0][y]\n",
    "z_value = critical_points[0][z]\n",
    "\n",
    "# 重新代入体积函数计算最大体积，确保计算准确\n",
    "max_volume = V.subs({x: x_value, y: y_value, z: z_value})\n",
    "\n",
    "print(f\"当长方体的长为{x_value.evalf()}，宽为{y_value.evalf()}，高为{z_value.evalf()}时，体积最大，最大体积为{max_volume.evalf()}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
